/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package Server;

/**
 *
 * @author MinhVu
 */

				
//**************************************
// Name: Java Web Proxy
// Description:This is a web proxy written in java; it works like the free version on www.megaproxy.com but I need help on it. Sometimes, whenever you download a page, it will say "connection to server reset" but I have not found problems in the code.
// By: James Pruitt
//
//This code is copyrighted and has// limited warranties.Please see http://www.Planet-Source-Code.com/vb/scripts/ShowCode.asp?txtCodeId=5269&lngWId=2//for details.//**************************************

import java.io.*;
import java.net.*;
 
public class Proxy_demo1 
{
	public static void main(String[] args)
	{
		try
		{
		ServerSocket host = new ServerSocket(1234);
		Socket s;
		ProxyThread x=null;
		while (true)
		{
			System.out.println("Active Threads: "+ProxyThread.activeThreads());
			s = host.accept();
			x = new ProxyThread(s);
			s = null;;
		}
		}catch (Exception e)
		{
		}
	}
}
class ProxyThread extends Thread
{
	static final String fetch = new String("proxy/");
	static final int kiloByte = 1024;
	static final int megaByte = 1024*kiloByte;
	static final int downloadChunk = 16*kiloByte;
	static final boolean outputStatus = true;
	private static int threadCount;
	private static int activeCount;
	private DataInputStream clientIn;
	private DataInputStream serverIn;
	private PrintStream		clientOut;
	private URL				server;
	private URLConnection	serverConn;
	private Socket			client;
	private String			headerBuffer;
	private String			requestURL;
	private String			contentType;
	private String			htmlBuffer;
	private String			serverAddress;
	private byte[] readBuffer = new byte[downloadChunk];
	private byte[] dataBuffer = new byte[0];
	private byte[] copyBuffer = new byte[0];
	private int threadID;
	private int threadStatus;
	
	public static int activeThreads()
	{
		return activeCount;
	}
	public ProxyThread(Socket s, String a)
	{
		threadCount++;
		threadID = threadCount;
		System.out.println("("+threadID+") "+"Setting up");
		try
		{
			client = s;
			client.setSoTimeout(10000);
			clientIn = new DataInputStream(client.getInputStream());
			clientOut = new PrintStream(client.getOutputStream());
		}catch (Exception e)
		{
		}
		serverAddress = a;
		this.start();
	}
	public ProxyThread(Socket s)
	{
		threadCount++;
		threadID = threadCount;
		System.out.println("("+threadID+") "+"Setting up");
		try
		{
			client = s;
			client.setSoTimeout(10000);
			clientIn = new DataInputStream(client.getInputStream());
			clientOut = new PrintStream(client.getOutputStream());
		}catch (Exception e)
		{
		}
		this.start();
	}
	private void setStatus(int s)
	{
		threadStatus = s;
		if (outputStatus)
		{
			// Implement
		}
	}
	public void send404()
	{
		clientOut.print("HTTP/1.0 404\r\n");
		clientOut.print("Connection: close\r\n");
		clientOut.print("Server: Java Proxy 1.3\r\n");
		clientOut.print("\r\n");
		clientOut.flush();
		clientOut.close();
		try
		{
			clientIn.close();
			client.close();
		}catch (Exception e)
		{
		}
	}
	public void run()
	{
		activeCount++;
		String hostBuffer;
		int tmpOne;
		int tmpTwo;
		int tmpThree;
		int tmpFour;
		int bytesRead = 0;
		if (serverAddress==null)
		{
			try
			{
				hostBuffer = InetAddress.getLocalHost().toString();
				hostBuffer = hostBuffer.substring(hostBuffer.indexOf("/")+1);
				serverAddress = "http://"+hostBuffer+"/";
			}catch (Exception e)
			{
			}
		}
		
		System.out.println("("+threadID+") "+"Server Address:"+serverAddress);
		while(true)
		{
			System.out.println("("+threadID+") "+"Reading HTTP headers");
			try
			{
				tmpFour = clientIn.available();
			}catch (Exception e)
			{
				tmpFour = 0;
			}
			if (tmpFour>0)
			{
				try
				{
					headerBuffer = clientIn.readLine();
					bytesRead+=headerBuffer.length();
					if (bytesRead>512)
					{
						activeCount--;
						send404();
						return;
					}
				}catch (Exception e)
				{
					activeCount--;
					send404();
					return;
				}
				tmpOne = headerBuffer.indexOf("GET");
				tmpTwo = headerBuffer.indexOf(fetch);
				tmpThree = headerBuffer.indexOf("HTTP/");
				System.out.println("("+threadID+") "+""+headerBuffer);
				if (tmpOne==0 && tmpTwo!=-1)
				{
					if ((tmpThree<tmpOne)||(tmpTwo<tmpOne)||(tmpThree<tmpTwo))
					{
						activeCount--;
						send404();
						return;
					}
					tmpTwo+=fetch.length();
					tmpThree--;
					requestURL = headerBuffer.substring(tmpTwo,tmpThree).trim();
					// Debugging
					System.out.println("("+threadID+") "+"requestURL="+requestURL+"*");
					break;
				}else if(tmpOne==0 && (tmpTwo==-1 || tmpThree==-1))
				{
					activeCount--;
					send404();
					return;
				}
			}
		}
		try
		{
			server = new URL(requestURL);
			serverConn = server.openConnection();
			serverIn = new DataInputStream(serverConn.getInputStream());
		}catch (Exception e)
		{
			activeCount--;
			send404();
			return;
		}
		tmpOne = 0;
		tmpTwo = 0;
		contentType = serverConn.getContentType();
		dataBuffer = new byte[0];
		System.out.println("("+threadID+") "+"Downloading data");
		while (tmpOne!=-1)
		{
			try
			{
				tmpOne = serverIn.read(readBuffer);
				if (tmpOne!=-1)
				{
					tmpTwo+= tmpOne;
					System.out.println("("+threadID+") "+"Chunk Downloaded; Size: "+tmpOne);
					if (dataBuffer.length==0)
					{
						dataBuffer = new byte[tmpOne];
						System.arraycopy(readBuffer,0,dataBuffer,0,tmpOne);
					}else
					{
						copyBuffer = new byte[tmpOne+dataBuffer.length];
						System.arraycopy(dataBuffer,0,copyBuffer,0,dataBuffer.length);
						System.arraycopy(readBuffer,0,copyBuffer,dataBuffer.length,tmpOne);
						dataBuffer = copyBuffer;
						copyBuffer = null;
					}
				}
			}catch (Exception e)
			{
				activeCount--;
				send404();
				return;
			}
		}
		System.out.println("("+threadID+") "+"Download complete");
		System.out.println("("+threadID+") "+"Sending data header");
		System.out.println("("+threadID+") "+"Content Type: "+contentType);
		clientOut.print("HTTP/1.0 200 OK\r\n");
		clientOut.print("Connection: close\r\n");
		clientOut.print("Server: Java Proxy\r\n");
		clientOut.print("Content-Type: "+contentType+"\r\n");
		clientOut.print("\r\n");
		if (contentType.indexOf("text/html")!=-1)
		{
			System.out.println("("+threadID+") "+"Parsing HTML document");
			htmlBuffer = parseHTML(new String(dataBuffer));
			System.out.println("("+threadID+") "+"Sending data");
			clientOut.print(htmlBuffer);
		}else
		{
			System.out.println("("+threadID+") "+"Sending data");
			try
			{
				clientOut.write(dataBuffer);
			}catch (Exception e)
			{
			}
		}
		clientOut.flush();
		clientOut.close();
		try
		{
			clientIn.close();
			client.close();
		}catch (Exception e)
		{
		}
		System.out.println("("+threadID+") "+"Done.");
		activeCount--;
	}
	public String parseHTML(String htmlText)
	{
		final int looseTag = 0;
		final int doubleQuotes = 1;
		final int singleQuotes = 2;
		final int parentheses = 3;
		
		String westBuffer="";
		String eastBuffer="";
		String tagContents;
		String fileString;
		String urlBuffer;
		String caseBuffer;
		String urlValid = "QWERTYUIOPASDFGHJKLZXCVBNM~@#%&_-+1234567890:;?/.*";
		String urlValidEnd = urlValid+"=";
		String styleValid = urlValid+"=\\";
		
		boolean styleTag = false;
		
		char letter=' ';
		char starter=' ';
		int tagOpen = 0;
		int tagClose = 0;
		int tagPos = 0;
		int posMark = 0;
		int firstQuote = 0;
		int lastQuote = 0;
		int encaseType = 0;
		int tmpOne = 0;
		int tmpTwo = 0;
		int tmpThree = 0;
		int searchMark;
		int firstChar = 0;
		int lastChar = 0;
		
		requestURL = requestURL.substring(7);
		tmpOne = requestURL.indexOf("?");
		tmpTwo = requestURL.indexOf("/");
		
		if (tmpOne!=-1 && tmpTwo<tmpOne && tmpTwo!=-1)
		{
			requestURL = requestURL.substring(0,tmpOne);
			urlBuffer = requestURL.substring(0,tmpTwo+1);
		}else if (tmpTwo!=-1)
		{
			urlBuffer = requestURL.substring(0,tmpTwo+1);
		}else
		{
			urlBuffer = requestURL;
		}
		urlBuffer = "http://"+urlBuffer;
		if (urlBuffer.charAt(urlBuffer.length()-1)!='/')
		{
			urlBuffer+="/";
		}
		
		while (true)
		{
			// Find an HTML tag
			tagOpen = htmlText.indexOf("<",posMark);
			tagClose = htmlText.indexOf(">",tagOpen);
			if (tagClose == -1 || tagOpen ==-1)
			{
				break;
			}
			
			posMark = tagClose;
			
			// Extract the tag contents
			tagContents = htmlText.substring(tagOpen,tagClose+1);
			
			
			// Look for different tags that reference to files
			
			styleTag = false;
			if (tagContents.indexOf("href")!=-1)
			{
				searchMark = tagContents.indexOf("href")+4;
			}else if (tagContents.indexOf("src")!=-1)
			{
				searchMark = tagContents.indexOf("src")+3;
			}else if (tagContents.indexOf("action")!=-1)
			{
				searchMark = tagContents.indexOf("action")+6;
			}else if (tagContents.indexOf("style")!=-1)
			{
				searchMark = tagContents.indexOf("style")+5;
				styleTag = true;
			}else
			{
				continue;
			}
			
			tagContents = tagContents.toUpperCase();
			
			firstChar = -1;
			lastChar = -1;
			
			for (int c = searchMark;c<tagContents.length();c++)
			{
				letter = tagContents.charAt(c);
				if (urlValid.indexOf(letter)!=-1)
				{
					firstChar = c+tagOpen;
					starter = tagContents.charAt(c-1);
					break;
				}
			}
			
			if (firstChar==-1)
			{
				continue;
			}
			
			for (int c = firstChar+1-tagOpen;c<tagContents.length();c++)
			{
				letter = tagContents.charAt(c);
				if(letter==' ' && starter!='=' && starter!=' ')
				{
					continue;
				}else if (urlValidEnd.indexOf(letter)==-1)
				{
					lastChar = c-1+tagOpen;
					break;
				}
			}
			
			if (lastChar==-1)
			{
				continue;
			}
			
			westBuffer = htmlText.substring(0,firstChar);
			eastBuffer = htmlText.substring(lastChar+1);
			fileString = htmlText.substring(firstChar,lastChar+1).trim();
			if (fileString.indexOf("../")!=-1)
			{
				System.out.println("requestURL="+requestURL);
				System.out.println(urlBuffer);
				fileString = serverAddress+fetch+pathMerge(urlBuffer,fileString);
			}else if (fileString.indexOf("http://")==0)
			{
				fileString = serverAddress+fetch+fileString;
			}else if (fileString.charAt(0)!='/')
			{
				fileString = serverAddress+fetch+urlBuffer+fileString;
			}else
			{
				fileString = fileString.substring(1);
				fileString = serverAddress+fetch+urlBuffer+fileString;
			}
			htmlText = westBuffer+fileString+eastBuffer;
		}
		return htmlText;
	}
	
	private String pathMerge(String absolute,String relative)
	{
		System.out.println("merge...");
		String tmpString = absolute.substring(8);
		int tmpOne=0;
		if (tmpString.indexOf('/')!=tmpString.lastIndexOf('/'))
		{
			try
			{
				URL urlAbs = new URL(absolute);
				if (relative.charAt(0)=='/')
				{
					relative = relative.substring(1);
				}
				URL relPath = new URL(urlAbs,relative);
				System.out.println(urlAbs);
				System.out.println(relPath);
				return relPath.toString();
			}catch (Exception e)
			{
				return "error!";
			}
		}else
		{
			tmpOne = relative.lastIndexOf("../");
			relative = relative.substring(tmpOne+1);
			return absolute+relative;
		}
	}
}